﻿using Lib.WPF.Core;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using System.Windows.Media;
using System.Xml.Linq;

namespace Lib.WPF.Dependents {
    /// <summary>
    /// 
    /// </summary>
    public class Draggable {

        /// <summary>
        /// 
        /// </summary>
        public static readonly DependencyProperty IsDraggableProperty = DependencyProperty.RegisterAttached("IsDraggable", typeof(bool), typeof(Draggable), new PropertyMetadata(false, OnIsDraggableChanged));
        /// <summary>
        /// 
        /// </summary>
        public static bool GetIsDraggable(DependencyObject obj) => (bool)obj.GetValue(IsDraggableProperty);
        /// <summary>
        /// 
        /// </summary>
        public static void SetIsDraggable(DependencyObject obj, bool value) => obj.SetValue(IsDraggableProperty, value);
        /// <summary>
        /// 
        /// </summary>
        private static void OnIsDraggableChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e) {
            if (obj is FrameworkElement element) {
                if ((bool)e.NewValue) {
                    element.MouseLeftButtonDown += Element_MouseLeftButtonDown;
                    element.MouseMove += Element_MouseMove;
                    element.MouseLeftButtonUp += Element_MouseLeftButtonUp;
                } else {
                    element.MouseLeftButtonDown -= Element_MouseLeftButtonDown;
                    element.MouseMove -= Element_MouseMove;
                    element.MouseLeftButtonUp -= Element_MouseLeftButtonUp;
                }
            }
        }
        /// <summary>
        /// 
        /// </summary>
        private static bool _isDragging = false;
        /// <summary>
        /// 
        /// </summary>
        private static Point _StartPoint;
        /// <summary>
        /// 
        /// </summary>
        private static FrameworkElement _DraggedElement;
        /// <summary>
        /// 
        /// </summary>
        private static TranslateTransform _Transform;
        /// <summary>
        /// 
        /// </summary>
        /// <param name="Sender"></param>
        /// <param name="e"></param>
        private static void Element_MouseLeftButtonDown(object Sender, MouseButtonEventArgs e) {
            _DraggedElement = Sender as FrameworkElement;
            if (_DraggedElement == null) { 
                return; 
            }
            _isDragging = true;
            _StartPoint = e.GetPosition(GetParentContainer(_DraggedElement));
            _DraggedElement.CaptureMouse();
            _Transform = _DraggedElement.RenderTransform as TranslateTransform;
            if (_Transform == null) {
                _Transform = new TranslateTransform();
                _DraggedElement.RenderTransform = _Transform;
            }
        }
        /// <summary>
        /// 
        /// </summary>
        private static void Element_MouseMove(object Sender, MouseEventArgs e) {
            if (_isDragging && _DraggedElement != null) {
                var Container = GetParentContainer(_DraggedElement);
                Point CurrentPoint = e.GetPosition(Container);
                double offsetX = CurrentPoint.X - _StartPoint.X;
                double offsetY = CurrentPoint.Y - _StartPoint.Y;
                if (_Transform == null) {
                    _Transform = new TranslateTransform();
                    _DraggedElement.RenderTransform = _Transform;
                }
                _Transform.X += offsetX;
                _Transform.Y += offsetY;
                _StartPoint = CurrentPoint;
            }
        }
        /// <summary>
        /// 
        /// </summary>
        private static void Element_MouseLeftButtonUp(object Sender, MouseButtonEventArgs e) {
            _isDragging = false;
            _DraggedElement?.ReleaseMouseCapture();
            _DraggedElement = null;
        }
        /// <summary>
        /// 
        /// </summary>
        private static FrameworkElement GetParentContainer(FrameworkElement obj) {
            while (obj != null && !(obj is Popup || obj is Panel || obj is Window)) {
                obj = LogicalTreeHelper.GetParent(obj) as FrameworkElement;  
            }
            return obj ?? Application.Current.MainWindow;
        }
    }
}
